// 事件类型
export type EventType = string | symbol;

// 事件处理器函数
export type Handler<T = unknown> = (...event: Array<T>) => void;

// 事件处理器列表
export type EventHandlerList<T = unknown> = Array<Handler<T>>;

// 事件处理器Map
export type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<
  keyof Events,
  EventHandlerList<Events[keyof Events]>
>;

export default class EventBus<Events extends Record<EventType, unknown>> {
  /**
   * 存储所有事件数据的Map对象
   */
  private all: EventHandlerMap<Events> = new Map();

  // 使用$开头的别名
  $on = this.on;
  $off = this.off;
  $emit = this.emit;
  $once = this.once;

  /**
   * 一次性监听事件
   * @param {string} type
   * @param {Function} handler
   */
  once<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>) {
    let fn = (...args: any[]) => {
      // 执行一次，立刻解除监听
      this.off(type, fn);
      handler(...args);
    };
    this.on(type, fn);
  }


  /**
   * 监听事件
   * @param {string} type
   * @param {Function} handler
   */
  on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>) {
    const handlers: Array<Handler<Events[Key]>> | undefined = (this.all)!.get(type);
    if (handlers) {
      handlers.push(handler);
    }
    else {
      (this.all)!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
    }
  }

  /**
   * 销毁事件监听
   * @param {string} type
   * @param {Function} handler
   */
  off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>) {
    const handlers: Array<Handler<Events[Key]>> | undefined = (this.all)!.get(type);
    if (handlers) {
      if (handler) {
        // “>>>”为无符号右移运算符，兼容indexOf === -1的情况（即不操作删除）
        handlers.splice(handlers.indexOf(handler) >>> 0, 1);
      }
      else {
        (this.all)!.set(type, []);
      }
    }
  }

  /**
   * 发送事件
   * @param {string} type
   * @param {any} evt
   */
  emit<Key extends keyof Events>(type: Key, ...evt: Array<Events[Key]>) {
    const handlers = (this.all)!.get(type);
    if (handlers) {
      (handlers as EventHandlerList<Events[keyof Events]>)
      .slice()
      .map((handler) => {
        handler(...evt);
      });
    }
  }
}
